﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Net.NetworkInformation;
using System.Net;
using System.Windows.Forms;

namespace PP.PaintLib
{
    public class DrawBoard : IDisposable
    {
        /// <summary>
        /// 构造函数：初始化画板
        /// </summary>
        /// <param name="picBox">传入作为画板的控件：PictureBox</param>
        public DrawBoard(PictureBox picBox)
        {
            if (picBox != null)
            {
                if (picBox.Image != null)
                {
                    image = picBox.Image;
                }
                else image = new Bitmap(picBox.Width, picBox.Height);
                this.picBox = picBox;
                this.picBox.Size = image.Size;//pic控件初始化 跟随图片大小
            }
        }

        #region 私有属性
        /// <summary>
        /// 历史图形
        /// </summary>
        private List<Image> Steps = new List<Image>();
        private int stepIndex = -1;
        private int stepMaxCount = 10;

        /// <summary>
        /// 绘图图形
        /// </summary>
        private DrawType drawType { get; set; } = DrawType.Line;

        /// <summary>
        /// 画笔
        /// </summary>
        private Pen pen => new Pen(ppColor, ppWidth);

        /// <summary>
        /// 宽度
        /// </summary>
        private float ppWidth = 1;

        /// <summary>
        /// 颜色
        /// </summary>
        private Color ppColor = Color.Black;

        /// <summary>
        /// 字体样式
        /// </summary>
        private Font ppFont = new Font("宋体", 9, FontStyle.Regular);
        /// <summary>
        /// 文本
        /// </summary>
        private string ppText = "";
        
        /// <summary>
        /// 圆角矩形-圆角值
        /// </summary>
        private float cornerRadius = 10;

        /// <summary>
        /// 起点、终点
        /// </summary>
        private Point StartPoint = Point.Empty, EndPoint = Point.Empty;

        /// <summary>
        /// 创建一个Image对象，用于绘制图像
        /// </summary>
        public Image image = null;

        /// <summary>
        /// 画布原型
        /// </summary>
        private PictureBox picBox = null;

        /// <summary>
        /// 平滑线模式
        /// </summary>
        private bool smoothLineMode = false;

        #endregion

        #region 公共方法

        /// <summary>
        /// 界面画布大小发生改变时，重画图形
        /// </summary>
        /// <param name="picBox"></param>
        public void ReloadPictures(PictureBox picBox,int width,int height)
        {
            if (image != null)
            {
                // 创建一个新的 Bitmap 对象作为临时画布
                Bitmap tempBmp = new Bitmap(width, height);

                // 使用 Graphics.FromImage 方法创建 Graphics 对象
                using (Graphics targetGraphics = Graphics.FromImage(tempBmp))
                {
                    // 使用 Graphics.DrawImage 方法将源图像绘制到临时画布上，指定源图像在临时画布中的位置和大小
                    targetGraphics.DrawImage(image, new Rectangle(Point.Empty, new Size(image.Width, image.Height)));
                }

                image = tempBmp;

                //临时画布的 Bitmap 对象
                picBox.Image = image;
            }
        }

        /// <summary>
        /// 线段绘制平滑处理
        /// </summary>
        /// <param name="flag">线段绘制是否平滑处理</param>
        public void SetSmoothLineMode(bool flag)
        {
            smoothLineMode = flag;
        }

        /// <summary>
        /// 线段平滑处理
        /// </summary>
        /// <param name="graph">绘图对象</param>
        public void SetLineMode(Graphics graph)
        {
            if(smoothLineMode)
            {
                graph.SmoothingMode = SmoothingMode.HighQuality;
                graph.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graph.PixelOffsetMode = PixelOffsetMode.HighQuality;
                graph.TextRenderingHint = TextRenderingHint.AntiAlias;
            }
            else
            {
                graph.SmoothingMode = SmoothingMode.Default;
                graph.InterpolationMode = InterpolationMode.Default;
                graph.PixelOffsetMode = PixelOffsetMode.Default;
                graph.TextRenderingHint = TextRenderingHint.SystemDefault;
            }
        }

        /// <summary>
        /// 设置绘画选择类型
        /// </summary>
        /// <param name="type"></param>
        public void SetSelectType(DrawType type)
        {
            drawType = type;
        }

        /// <summary>
        /// 设置起始点
        /// </summary>
        /// <param name="start"></param>
        public void SetStartPoint(Point start)
        {
            this.StartPoint = start;
        }

        /// <summary>
        /// 设置结束点
        /// </summary>
        /// <param name="end"></param>
        public void SetEndPoint(Point end)
        {
            this.EndPoint = end;
        }

        /// <summary>
        /// 设置线宽
        /// </summary>
        /// <param name="penWidth"></param>
        public void SetPenWidth(float width)
        {
            ppWidth = width;
        }

        /// <summary>
        /// 设置圆角矩形-圆角值
        /// </summary>
        /// <param name="penWidth"></param>
        public void SetCornerRadiusValue(float cornerRadiusValue)
        {
            cornerRadius = cornerRadiusValue;
        }

        /// <summary>
        /// 设置线颜色
        /// </summary>
        /// <param name="penColor"></param>
        public void SetPenColor(Color penColor)
        {
            ppColor = penColor;
        }

        /// <summary>
        /// 设置文本
        /// </summary>
        /// <param name="text">文本</param>
        public void SetText(string text)
        {
            ppText = text;
        }

        /// <summary>
        /// 设置字体
        /// </summary>
        /// <param name="font">字体</param>
        public void SetFont(Font font)
        {
            ppFont = font;
        }

        /// <summary>
        /// 在指定画布中绘图
        /// </summary>
        /// <param name="g">要绘制的图形</param>
        public void Draw(Graphics g = null)
        {

            if (g == null)//是null在底图上绘制，否则就是在窗口上绘制
            {
                g = Graphics.FromImage(image);
            }

            SetLineMode(g);
            switch (drawType)
            {
                //工具类型
                case DrawType.Eraser:
                    using (Pen tempPen = (Pen)pen.Clone())
                    {
                        tempPen.Color = picBox.BackColor;
                        g.DrawLine(tempPen, StartPoint, EndPoint);
                    }
                    break;
                case DrawType.LineTo:
                    g.DrawLine(pen, StartPoint, EndPoint);
                    break;
                case DrawType.Writing:
                    g.DrawString(ppText, ppFont, new SolidBrush(ppColor), StartPoint.X, StartPoint.Y);
                    ppText = "";//用完后要清空文本
                    break;


                //绘制图形
                case DrawType.Line://绘制线段
                    g.DrawLine(pen, StartPoint, EndPoint);
                    break;
                case DrawType.Circle://绘制圆形
                    g.DrawEllipse(pen, GetRectangle(StartPoint, EndPoint));
                    break;
                case DrawType.Rectangle://绘制矩形
                    g.DrawRectangle(pen, GetRectangle(StartPoint, EndPoint));
                    break;
                case DrawType.Diamond:// 绘制菱形
                    g.DrawPolygon(pen, GetDiamondPoints(StartPoint, EndPoint));
                    break;
                case DrawType.Triangle://绘制三角形
                    g.DrawPolygon(pen, GetTrianglePoints(StartPoint, EndPoint));
                    break;
                case DrawType.RoundRectangle://绘制圆角矩形
                    g.DrawPath(pen, GetRoundRectanglePath(GetRectangle(StartPoint, EndPoint), cornerRadius));
                    break;
                case DrawType.Arrow:
                    Point[] ArrowPoints = GetArrowPoints(StartPoint, EndPoint);//三角形顶点坐标
                    g.DrawLine(pen, StartPoint, GetArrowCenterPoint(ArrowPoints));//绘制线段
                    g.FillPolygon(new SolidBrush(pen.Color), ArrowPoints);//绘制箭头
                    break;
                case DrawType.Pentagon://绘制五角形
                    g.DrawPolygon(pen, GetPentagonPoints(StartPoint, EndPoint));
                    break;
                case DrawType.Hexagon://绘制六形
                    g.DrawPolygon(pen, GetHexagonPoints(StartPoint, EndPoint));
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// 清理画布
        /// </summary>
        public void Clear()
        {
            image?.Dispose();
            image = new Bitmap(picBox.Width, picBox.Height);
            this.picBox.Image = image;
        }

        /// <summary>
        /// 显示底层绘图 到界面上
        /// </summary>
        public void ShowImage()
        {
            this.picBox.Image = image;
        }

        /// <summary>
        /// 退回上一步
        /// </summary>
        public void GoBack()
        {
            if (stepIndex - 1 < 0 || stepIndex - 1 >= Steps.Count)
            {
                return;
            }

            image = Steps[--stepIndex];
            picBox.Image = image;//重绘界面 需要处理不需要添加图形的情况
        }

        /// <summary>
        /// 进入下一步
        /// </summary>
        public void GoForward()
        {
            if (stepIndex + 1 < 0 || stepIndex + 1 >= Steps.Count)
            {
                return;
            }

            image = Steps[++stepIndex];
            picBox.Image = image;//重绘界面 需要处理不需要添加图形的情况
        }

        /// <summary>
        /// 记录历史步骤
        /// </summary>
        /// <param name="image"></param>
        public void RecordHistory(Image image)
        {
            if (image == null || Steps.Contains(image))
            {
                return;
            }
            if (Steps.Count > stepMaxCount)
            {
                var img = Steps[0];
                Steps.Remove(img);
                img?.Dispose();
            }
            Steps.Add(image);
            stepIndex = Steps.Count - 1;//更新索引
        }

        #endregion

        #region 绘图细节

        /// <summary>
        /// 计算箭头的顶点坐标
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        private Point[] GetArrowPoints(Point start, Point end)
        {
            float arrowLength = pen.Width * 10; //箭头长度
            float arrowAngle = (float)Math.PI / 6; //箭头角度，30度转为弧度

            //计算箭头线段的倾斜角度
            float angle = (float)Math.Atan2(end.Y - start.Y, end.X - start.X);

            //计算箭头三角形的顶点坐标
            Point[] arrowPoints = new Point[3];
            arrowPoints[0] = end;
            arrowPoints[1] = new Point(
                (int)(end.X - (arrowLength * (float)Math.Cos(angle - arrowAngle))),
                (int)(end.Y - (arrowLength * (float)Math.Sin(angle - arrowAngle))));
            arrowPoints[2] = new Point(
                (int)(end.X - (arrowLength * (float)Math.Cos(angle + arrowAngle))),
                (int)(end.Y - (arrowLength * (float)Math.Sin(angle + arrowAngle))));

            return arrowPoints;
        }

        /// <summary>
        /// 计算箭头的中心点坐标
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        private Point GetArrowCenterPoint(Point[] points)
        {
            if (points.Length < 3) return Point.Empty;
            int x = (points[0].X + points[1].X + points[2].X) / 3;
            int y = (points[0].Y + points[1].Y + points[2].Y) / 3;
            return new Point(x, y);
        }

        /// <summary>
        /// 矩形（x,y,width,height）
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        private Rectangle GetRectangle(Point start, Point end)
        {
            int x = Math.Min(start.X, end.X);
            int y = Math.Min(start.Y, end.Y);
            int width = Math.Abs(start.X - end.X);
            int height = Math.Abs(start.Y - end.Y);
            return new Rectangle(x, y, width, height);
        }

        /// <summary>
        /// 计算三角形的顶点坐标
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        private Point[] GetTrianglePoints(Point start, Point end)
        {
            Point[] trianglePoints = new Point[3];
            trianglePoints[0] = new Point(start.X, (start.Y + end.Y) / 2);
            trianglePoints[1] = new Point((start.X + end.X) / 2, start.Y);
            trianglePoints[2] = new Point(end.X, (start.Y + end.Y) / 2);
            return trianglePoints;
        }

        /// <summary>
        /// 菱形（top,right,bottom,left）
        /// </summary>
        /// <param name="startPoint"></param>
        /// <param name="endPoint"></param>
        /// <returns></returns>
        private Point[] GetDiamondPoints(Point start, Point end)
        {
            Point[] diamondPoints = new Point[4];  // 定义菱形四个点的坐标
            diamondPoints[0] = new Point((start.X + end.X) / 2, start.Y);  // 上顶点
            diamondPoints[1] = new Point(end.X, (start.Y + end.Y) / 2);    // 右顶点
            diamondPoints[2] = new Point((start.X + end.X) / 2, end.Y);    // 下顶点
            diamondPoints[3] = new Point(start.X, (start.Y + end.Y) / 2);  // 左顶点
            return diamondPoints;
        }

        /// <summary>
        /// 六边形（1，2，3，4，5，6）
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        private Point[] GetHexagonPoints(Point start, Point end)
        {
            // 计算鼠标位置相对于起始点的偏移量
            int offsetX = end.X - StartPoint.X;
            int offsetY = end.Y - StartPoint.Y;

            // 计算六边形的顶点坐标
            Point[] points = new Point[6];
            points[0] = StartPoint;
            points[1] = new Point(StartPoint.X + offsetX / 2, StartPoint.Y - offsetY);
            points[2] = new Point(StartPoint.X + offsetX, StartPoint.Y);
            points[3] = new Point(StartPoint.X + offsetX, StartPoint.Y + offsetY);
            points[4] = new Point(StartPoint.X + offsetX / 2, StartPoint.Y + offsetY * 2);
            points[5] = new Point(StartPoint.X, StartPoint.Y + offsetY);

            return points;
        }

        /// <summary>
        /// 五边形（1，2，3，4，5）
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        private Point[] GetPentagonPoints(Point start, Point end)
        {
            Point[] points = new Point[5]; // 五个顶点
            // 计算当前点和起点之间的距离
            double distance = Math.Sqrt(Math.Pow(end.X - start.X, 2) + Math.Pow(end.Y - start.Y, 2));

            // 计算五边形顶点坐标
            double angle = 2 * Math.PI / 5; // 每个顶角的角度
            for (int i = 0; i < 5; i++)
            {
                points[i].X = (int)(start.X + distance * Math.Sin(i * angle));
                points[i].Y = (int)(start.Y + distance * Math.Cos(i * angle));
            }

            return points;
        }

        /// <summary>
        /// 圆角矩形（矩形+圆角值）
        /// </summary>
        /// <param name="rectangle">矩形</param>
        /// <param name="cornerRadius">圆角值</param>
        /// <returns></returns>
        private GraphicsPath GetRoundRectanglePath(Rectangle rectangle, float cornerRadius)
        {
            // 创建圆角矩形的路径
            GraphicsPath path = new GraphicsPath();
            path.AddArc(rectangle.X, rectangle.Y, cornerRadius * 2, cornerRadius * 2, 180, 90);
            path.AddArc(rectangle.Right - cornerRadius * 2, rectangle.Y, cornerRadius * 2, cornerRadius * 2, 270, 90);
            path.AddArc(rectangle.Right - cornerRadius * 2, rectangle.Bottom - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 0, 90);
            path.AddArc(rectangle.X, rectangle.Bottom - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 90, 90);
            path.CloseFigure();
            return path;
        }

        #endregion


        /// <summary>
        /// 析构函数
        /// </summary>
        public void Dispose()
        {
            image?.Dispose();
            picBox?.Dispose();
        }
    }

    #region 枚举

    /// <summary>
    /// 绘画选择
    /// </summary>
    public enum DrawType
    {
        //绘画图形
        Line,//线段
        Circle,//圆形
        Rectangle,//矩形
        RoundRectangle,//圆角矩形
        Diamond,//菱形
        Triangle,//三角形
        Arrow,//箭头
        Pentagon,//五边形
        Hexagon,//六边形

        //绘画工具
        Eraser,//橡皮擦
        Writing,//文字
        LineTo,//线


    }

    #endregion

}
